﻿#region Using namespaces

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;

using BOL.Apps.GeneNetworkBrowser.Analysis;
using BOL.Apps.GeneNetworkBrowser.Analysis.SubNetwork;
using BOL.Apps.GeneNetworkBrowser.IO;
using BOL.Apps.GeneNetworkBrowser.Windows;
using BOL.Apps.GeneNetworkBrowser.ViewModels;

#endregion

namespace BOL.Apps.GeneNetworkBrowser.ViewModels
{
    public sealed class MainWindowViewModel : ViewModelBase
    {
        #region Private variables

        private IDialogProvider _dialogProvider;

        private string _message;
        private Visibility _progressBarVisibility;
        private int _currentProgress;
        
        private int? _vertexCount;
        private int? _edgeCount;

        private PaneViewModel _activePane = null;
        private DocumentViewModel _activeDocument = null;

        #endregion

        #region Public variables

        public string GeneNetworkBrowserFolder = 
            String.Format("{0}\\Gene Network Browser", Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));

        #endregion

        #region Public properties

        public ProjectPaneViewModel ProjectPaneViewModel { get; private set; }

        public PropertyPaneViewModel PropertyPaneViewModel { get; private set; }

        public AnnotationPaneViewModel AnnotationPaneViewModel { get; private set; }

        public OutputPaneViewModel OutputPaneViewModel { get; private set; }

        public FilterPaneViewModel FilterPaneViewModel { get; private set; }

        public AnalysisPaneViewModel AnalysisPaneViewModel { get; private set; }

        public ObservableCollection<PaneViewModel> Panes { get; private set; }

        public StartDocumentViewModel StartDocument { get; private set; } 

        public ObservableCollection<DocumentViewModel> Documents { get; private set; }

        /// <summary>Gets true if any of the open documents are modified.</summary>
        public bool AnyDocumentChanged
        {
            get
            {
                foreach (var document in Documents)
                    if (document.IsChanged)
                        return true;

                return false;
            }
        }

        /// <summary>Gets DocumentViewModel for the active document.</summary>
        public DocumentViewModel ActiveDocument
        {
            get { return _activeDocument; }
            set
            {
                if (value != _activeDocument)
                {
                    if (_activeDocument != null)
                        _activeDocument.Changed -= new EventHandler<EventArgs>(ActiveDocument_IsChanged);

                    _activeDocument = value;

                    if (_activeDocument != null)
                        _activeDocument.Changed += new EventHandler<EventArgs>(ActiveDocument_IsChanged);

                    OnPropertyChanged("ActiveDocument");
                    OnPropertyChanged("Title");

                    if (ActiveDocumentChanged != null)
                    {
                        if (_activeDocument is NetworkDocumentViewModel)
                        {
                            var network = ((NetworkDocumentViewModel)_activeDocument).Network;
                            
                            VertexCount = network.VertexCount;
                            EdgeCount = network.EdgeCount;

                            // NetworkInfoVisibility = ...
                        }

                        ActiveDocumentChanged(this, EventArgs.Empty);
                    }
                }
            }
        }

        /// <summary>Event raised when the ActiveDocument property has changed.</summary>
        public event EventHandler<EventArgs> ActiveDocumentChanged;

        /// <summary>Gets PaneViewModel for the active dockable window.</summary>
        public PaneViewModel ActivePane
        {
            get { return _activePane; }
            set
            {
                if (value != _activePane)
                {
                    _activePane = value;
                    OnPropertyChanged("ActivePane");
                }
            }
        }
        
        public override string Title
        { 
            get 
            { 
                return String.Format("Gene Network Browser - {0}", ProjectPaneViewModel.Title);
            }
        }

        public string Message
        {
            get { return _message; }
            set
            {
                if (value != _message)
                {
                    _message = value;
                    OnPropertyChanged("Message");
                }
            }
        }

        public int CurrentProgress
        {
            get { return _currentProgress; }
            set
            {
                if (_currentProgress != value)
                {
                    _currentProgress = value;
                    OnPropertyChanged("CurrentProgress");
                }
            }
        }

        public Visibility ProgressBarVisibility
        {
            get { return _progressBarVisibility; }
            set
            {
                if (_progressBarVisibility != value)
                {
                    _progressBarVisibility = value;
                    OnPropertyChanged("ProgressBarVisibility");
                }
            }
        }

        public int? VertexCount
        {
            get { return _vertexCount; }
            set
            {
                if (_vertexCount != value)
                {
                    _vertexCount = value;
                    OnPropertyChanged("VertexCount");
                }
            }
        }

        public int? EdgeCount
        {
            get { return _edgeCount; }
            set
            {
                if (_edgeCount != value)
                {
                    _edgeCount = value;
                    OnPropertyChanged("EdgeCount");
                }
            }
        }

        #endregion

        #region Constructor

        public MainWindowViewModel(IDialogProvider dialogProvider)
        {
            _dialogProvider = dialogProvider;

            ProjectPaneViewModel = new ProjectPaneViewModel(this);
            PropertyPaneViewModel = new PropertyPaneViewModel(this);
            AnnotationPaneViewModel = new AnnotationPaneViewModel(this);
            OutputPaneViewModel = new OutputPaneViewModel(this);
            FilterPaneViewModel = new FilterPaneViewModel(this);
            AnalysisPaneViewModel = new AnalysisPaneViewModel(this);

            Panes = new ObservableCollection<PaneViewModel>();
            Panes.Add(ProjectPaneViewModel);
            Panes.Add(PropertyPaneViewModel);
            Panes.Add(AnnotationPaneViewModel);
            Panes.Add(OutputPaneViewModel);
            Panes.Add(FilterPaneViewModel);
            Panes.Add(AnalysisPaneViewModel);

            // create document collection and add a start page viewmodel.
            Documents = new ObservableCollection<DocumentViewModel>();
            Documents.Add(new StartDocumentViewModel());

            _message = "Ready";
            _progressBarVisibility = Visibility.Collapsed;

            if (!Directory.Exists(GeneNetworkBrowserFolder))
                Directory.CreateDirectory(GeneNetworkBrowserFolder);
        }

        #endregion

        #region Document methods

        /// <summary>Create a new project document and add it to the viewmodel.</summary>
        public void NewProject()
        {
            string projectDir = _dialogProvider.NewProjectFileDialog();
            Directory.CreateDirectory(projectDir);

            Console.WriteLine(projectDir);

            //var vmProject = new ProjectPaneViewModel(this);
            //ProjectTreeViewItemViewModel("New Project");
            //
//            var vmNetwork = new NetworkDocumentViewModel(new Network(), string.Empty);
            //Documents.Add(vmNetwork);
        }

        public void OpenSubNetworks()
        {
            string[] fileNames = _dialogProvider.OpenNetworkFilesDialog();

            var subNetworks = new List<Network>();

            foreach (var fileName in fileNames)
            {
                var fi = new FileInfo(fileName);

                if (fi.Extension == ".xml" || fi.Extension == "*.xgmml")
                {
                    var graphMLR = new GraphMLReader(fileName);
                    var network = graphMLR.Read();
                    graphMLR.Close();
                    if (network.VertexCount > 0)
                        subNetworks.Add(network);
                }
            }

            try
            {
                var vmSubNetworksDocument = new SubNetworksDocumentViewModel(subNetworks, "Sub-networks");
                Documents.Add(vmSubNetworksDocument);
            }
            catch (Exception ex)
            {
                _dialogProvider.ErrorMessage(String.Format("Failed to open sub-network documents.\nException occurred:\n{1}", ex.Message));
            }
        }

        public void OpenFile()
        {
            string[] fileNames = _dialogProvider.OpenNetworkFilesDialog();

            foreach (var fileName in fileNames)
            {
                var fi = new FileInfo(fileName);

                if (fi.Extension == ".gxl")
                {
                    //var gxlR = new GXLReader(file);
                    //var network = gxlR.Read();
                    //gxlR.Close();
                    //if (network.VertexCount > 0)
                    //    OpenNetwork(network);
                }
                else if (fi.Extension == ".gml")
                {
                    //var gmlR = new GMLReader(file);
                    //var network = gmlR.Read();
                    //gmlR.Close();
                    //if (network.VertexCount > 0)
                    //    OpenNetwork(network);
                }
                else if (fi.Extension == ".xml" || fi.Extension == "*.xgmml")
                {
                    var graphMLR = new GraphMLReader(fileName);
                    var network = graphMLR.Read();
                    graphMLR.Close();
                    if (network.VertexCount > 0)
                        OpenNetwork(network, fileName);
                }
                else
                {
                    var grbR = new GRBReader(fileName);
                    var network = grbR.Read();
                    grbR.Close();
                    if (network.VertexCount > 0)
                        OpenNetwork(network, fileName);
                }
            }
        }

        public void OpenTextFile()
        {
            //string fileNames = _dialogProvider.OpenTextFileDialog();

            //foreach (var fileName in fileNames)
            //{
            //    var grbR = new GRBReader(fileName);
            //    var network = grbR.Read();
            //    grbR.Close();
            //    if (network.VertexCount > 0)
            //        OpenNetwork(network, fileName);
            //}
        }

        /// <summary>
        /// Open a file, create a view-model and it to the collection of document view-models.
        /// </summary>
        public void OpenFile(string filePath)
        {
            try
            {

                //var fileContent = File.ReadAllText(filePath);
                //var newDocumentViewModel = new DocumentViewModel(filePath, fileContent, false);
                //Documents.Add(newDocumentViewModel);
            }
            catch (Exception ex)
            {
                _dialogProvider.ErrorMessage(String.Format("Failed to open document {0}\nException occurred:\n{1}", filePath, ex.Message));
            }
        }

        public void OpenGeneValueFile(string propertyName)
        {
            string filePath = _dialogProvider.OpenGeneValueFileDialog();

            try
            {
                var network = (ActiveDocument as NetworkDocumentViewModel).Network;
                var gvr = new GeneValueReader(filePath, false, 1, new char[1] { '\t' });
                var gvrd = gvr.Read();

                foreach (var gene in network.Vertices)
                {
                    if (gvrd.ContainsKey(gene.Id))
                        gene.Attributes.Add(propertyName, gvrd[gene.Id][0]);
                    //gene.Attributes.Add(name, Math.Abs(gvrd[gene.Id][0]));
                    else
                        gene.Attributes.Add(propertyName, 0);
                }

                gvr.Close();

                //var fileContent = File.ReadAllText(filePath);
                //var newDocumentViewModel = new DocumentViewModel(filePath, fileContent, false);
                //Documents.Add(newDocumentViewModel);
            }
            catch (Exception ex)
            {
                _dialogProvider.ErrorMessage(String.Format("Failed to open document {0}\nException occurred:\n{1}", filePath, ex.Message));
            }
        }

        public void OpenNetwork(Network network, string filePath)
        {
            try
            {
                var vmNetworkDocument = new NetworkDocumentViewModel(network, filePath);
                Documents.Add(vmNetworkDocument);
            }
            catch (Exception ex)
            {
                _dialogProvider.ErrorMessage(String.Format("Failed to open document {0}\nException occurred:\n{1}", filePath, ex.Message));
            }
        }


        /// <summary>
        /// Save the active document, allowing the user to select a filename for new files.
        /// </summary>
        public void SaveFile()
        {
            SaveFile(ActiveDocument);
        }

        /// <summary>
        /// Save the specified document, allowing the user to select a filename for new files.
        /// </summary>
        public void SaveFile(DocumentViewModel document)
        {
            if (string.IsNullOrEmpty(document.FilePath))
            {
                SaveFileAs(document);
            }
            else
            {
                SaveFile(document, document.FilePath);
            }
        }

        /// <summary>
        /// Save the specified document to the specified filepath.
        /// </summary>
        public void SaveFile(DocumentViewModel document, string filePath)
        {
            try
            {
                //File.WriteAllText(filePath, document.Text);

                //document.FilePath = filePath;
                //document.IsChanged = false;
            }
            catch (Exception ex)
            {
                _dialogProvider.ErrorMessage(String.Format("Failed to save document {0}.\nException occurred:\n{1}", filePath, ex.Message));
            }
        }

        /// <summary>
        /// Save the active document as a new file, allowing the user to specify the new filepath.
        /// </summary>
        public void SaveFileAs()
        {
            SaveFileAs(ActiveDocument);
        }

        /// <summary>
        /// Save the specified document as a new file, allowing the user to specify the new filepath.
        /// </summary>
        public void SaveFileAs(DocumentViewModel document)
        {
            string filePath = _dialogProvider.SaveAsNetworkFileDialog();
            SaveFile(document, filePath);
        }

        /// <summary>
        /// Save all documents.
        /// </summary>
        public void SaveAllFiles()
        {
            foreach (var document in Documents)
            {
                SaveFile(document);
            }
        }

        /// <summary>
        /// Close the active document.
        /// </summary>
        public void CloseFile()
        {
            CloseFile(ActiveDocument);
        }

        /// <summary>
        /// Determine if the file can be closed.
        /// If the file is modified, but not saved, the user is asked
        /// to confirm that the document should be closed.
        /// </summary>
        public bool QueryCanCloseFile(DocumentViewModel document)
        {
            if (document.IsChanged)
                if (!_dialogProvider.QueryCloseModifiedDocument(document))
                    return false;

            return true;
        }

        /// <summary>
        /// Close the specified document.
        /// Returns 'true' if the user allowed the file to be closed,
        /// or 'false' if the user canceled closing of the file.
        /// </summary>
        public bool CloseFile(DocumentViewModel document)
        {
            if (!QueryCanCloseFile(document))
                return false;

            Documents.Remove(document);

            return true;
        }

        /// <summary>
        /// Close all open documents.
        /// </summary>
        public void CloseAllFiles()
        {
            var documents = Documents.ToArray();

            foreach (var document in documents)
                CloseFile(document);
        }

        /// <summary>
        /// Show all panes.
        /// </summary>
        public void ShowAllPanes()
        {
            foreach (var pane in Panes)
                pane.IsVisible = true;
        }

        /// <summary>
        /// Hide all panes.
        /// </summary>
        public void AutoHideAll()
        {
            foreach (var pane in Panes)
                pane.IsVisible = false;
        }

        /// <summary>
        /// Called when the application is closing.
        /// Return 'true' to allow application to exit.
        /// </summary>
        public bool OnApplicationClosing()
        {
            if (AnyDocumentChanged)
                if (!_dialogProvider.QueryCloseApplicationWhenDocumentsModified())
                    return false; // User has cancelled application exit.

            return true; // Allow application exit to proceed.
        }

        public void AddDocument(NetworkDocumentViewModel vmNetwork)
        {
            Documents.Add(vmNetwork);
        }

        public void AddDocument(SubNetworksDocumentViewModel vmSubNetworks)
        {
            Documents.Add(vmSubNetworks);
        }

        private void ActiveDocument_IsChanged(object sender, EventArgs e)
        {
            OnPropertyChanged("Title");
        }

        #endregion
    }
}
